package Q3_01_Three_in_One;
import java.util.EmptyStackException;
import CtCILibrary.AssortedMethods;
public class MultiStack {
/* StackInfo is a simple class that holds a set of data about
* each stack. It does not hold the actual items in the stack.
* We could have done this with just a bunch of individual
* variables, but that’s messy and doesn’t gain us much. */
private class StackInfo {
public int start, size, capacity;
public StackInfo(int start, int capacity) {
this.start = start;
this.capacity = capacity;
}
/* Check if an index on the full array is within the stack
* boundaries. The stack can wrap around to the start of
* the array. */
public boolean isWithinStackCapacity(int index) {
/* If outside of bounds of array, return false. */
if (index < 0 || index >= values.length) {
return false;
}
/* If index wraps around, adjust it. */
int contiguousIndex = index < start ? index + values.length : index;
int end = start + capacity;
return start <= contiguousIndex && contiguousIndex < end;
}
public int lastCapacityIndex() {
return adjustIndex(start + capacity - 1);
}
public int lastElementIndex() {
return adjustIndex(start + size - 1);
}
public boolean isFull() {
return size == capacity;
}
public boolean isEmpty() {
return size == 0;
}
}
private StackInfo[] info;
private int[] values;
public MultiStack(int numberOfStacks, int defaultSize) {
/* Create metadata for all the stacks. */
info = new StackInfo[numberOfStacks];
for (int i = 0; i < numberOfStacks; i++) {
info[i] = new StackInfo(defaultSize * i, defaultSize);
}
values = new int[numberOfStacks * defaultSize];
}
/* Returns the number of items actually present in stack. */
public int numberOfElements() {
int size = 0;
for (StackInfo sd : info) {
size += sd.size;
}
return size;
}
/* Returns true is all the stacks are full. */
public boolean allStacksAreFull() {
return numberOfElements() == values.length;
}
/* Adjust index to be within the range of 0 -> length - 1. */
private int adjustIndex(int index) {
/* Java's mod operator can return neg values. For example,
* (-11 % 5) will return -1, not 4. We actually want the
* value to be 4 (since we're wrapping around the index).
*/
int max = values.length;
return ((index % max) + max) % max;
}
/* Get index after this index, adjusted for wrap around. */
private int nextIndex(int index) {
return adjustIndex(index + 1);
}
/* Get index before this index, adjusted for wrap around. */
private int previousIndex(int index) {
return adjustIndex(index - 1);
}
/* Shift items in stack over by one element. If we have
* available capacity, then we'll end up shrinking the stack
* by one element. If we don't have available capacity, then
* we'll need to shift the next stack over too. */
private void shift(int stackNum) {
System.out.println("/// Shifting " + stackNum);
StackInfo stack = info[stackNum];
/* If this stack is at its full capacity, then you need
* to move the next stack over by one element. This stack
* can now claim the freed index. */
if (stack.size >= stack.capacity) {
int nextStack = (stackNum + 1) % info.length;
shift(nextStack);
stack.capacity++; // claim index that next stack lost
}
/* Shift all elements in stack over by one. */
int index = stack.lastCapacityIndex();
while (stack.isWithinStackCapacity(index)) {
values[index] = values[previousIndex(index)];
index = previousIndex(index);
}
/* Adjust stack data. */
values[stack.start] = 0; // Clear item
stack.start = nextIndex(stack.start); // move start
stack.capacity--; // Shrink capacity
}
/* Expand stack by shifting over other stacks */
private void expand(int stackNum) {
System.out.println("/// Expanding stack " + stackNum);
shift((stackNum + 1) % info.length);
info[stackNum].capacity++;
}
/* Push value onto stack num, shifting/expanding stacks as
* necessary. Throws exception if all stacks are full. */
public void push(int stackNum, int value) throws FullStackException {
System.out.println("/// Pushing stack " + stackNum + ": " + value);
if (allStacksAreFull()) {
throw new FullStackException();
}
/* If this stack is full, expand it. */
StackInfo stack = info[stackNum];
if (stack.isFull()) {
expand(stackNum);
}
/* Find the index of the top element in the array + 1,
* and increment the stack pointer */
stack.size++;
values[stack.lastElementIndex()] = value;
}
/* Remove value from stack. */
public int pop(int stackNum) throws Exception {
System.out.println("/// Popping stack " + stackNum);
StackInfo stack = info[stackNum];
if (stack.isEmpty()) {
throw new EmptyStackException();
}
/* Remove last element. */
int value = values[stack.lastElementIndex()];
values[stack.lastElementIndex()] = 0; // Clear item
stack.size--; // Shrink size
return value;
}
/* Get top element of stack.*/
public int peek(int stackNum) {
StackInfo stack = info[stackNum];
return values[stack.lastElementIndex()];
}
public int[] getValues() {
return values;
}
public int[] getStackValues(int stackNum) {
StackInfo stack = info[stackNum];
int[] items = new int[stack.size];
for (int i = 0; i < items.length; i++) {
items[i] = values[adjustIndex(stack.start + i)];
}
return items;
}
public String stackToString(int stackNum) {
int[] items = getStackValues(stackNum);
return stackNum + ": " + AssortedMethods.arrayToString(items);
}
}